<?php

namespace StatePattern;

/**
 * 状态模式的角色和职责
 * 1. 环境类(Context)：只有当前状态对象，负责状态的切换，同事对外暴露统一的操作接口
 * 2. 抽象状态类(State)：定义所有具体状态需要实现的接口或者抽象类。
 * 3. 具体状态类(ConcreteState)：实现抽象状态定义的行为，每个具体状态类封装了对应的操作逻辑，并可以决定是否流转到下一个状态类
 */

// 以订单流转为例，实现一个状态模式
// 订单状态有：待支付、已支付、待发货、已发货、完成

// 1. 首先定义了订单类和抽象状态类
interface OrderState
{
    public function handleRequest(Order $order): void;
}
class Order
{
    private OrderState $orderState;

    /**
     * @param OrderState $orderState
     */
    public function __construct(OrderState $orderState)
    {
        $this->orderState = $orderState;
    }

    /**
     * @param OrderState $orderState
     */
    public function setOrderState(OrderState $orderState): void
    {
        $this->orderState = $orderState;
    }

    public function request(): void
    {
        $this->orderState->handleRequest($this);
    }
}

// 2. 实现各个订单状态的状态类，不同的状态类有不同的操作逻辑，并可以指定流转的下一个状态
// 待支付状态
class PendingState implements OrderState
{
//    private Order $order;
    public function handleRequest(Order $order): void
    {
        echo "订单处于待支付状态，请支付 \n";
        // 流转为支付完成状态
        $order->setOrderState(new PaidState());
    }
}
// 支付完成状态
class PaidState implements OrderState
{
    public function handleRequest(Order $order): void
    {
        echo "订单已支付，待发货 \n";
        // 流转为支付完成状态
        $order->setOrderState(new PendingDeliveryState());
    }
}
// 待发货状态
class PendingDeliveryState implements OrderState
{
    public function handleRequest(Order $order): void
    {
        echo "订单处于待发货状态，请发货 \n";
        // 流转为支付完成状态
        $order->setOrderState(new DeliveredState());
    }
}
// 已发货状态
class DeliveredState implements OrderState
{
    public function handleRequest(Order $order): void
    {
        echo "订单已发货，请等待接收 \n";
        // 流转为支付完成状态
        $order->setOrderState(new CompletedState());
    }
}
// 订单已完成
class CompletedState implements OrderState
{
    public function handleRequest(Order $order): void
    {
        echo "订单已完成 \n";
    }
}

// 测试
$order = new Order(new PendingState());
$order->request();
$order->request();
$order->request();
$order->request();
$order->request();
$order->request();
$order->request();
$order->request();